/**
 * 
 */
package subscribersManagement;

import hibernateUtils.HibernateUtil;

import java.io.Serializable;
import java.util.Calendar;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.Transient;

import org.hibernate.Session;
import org.hibernate.annotations.Cascade;

import daos.DAOs;

import exceptions.BadParametersException;
import exceptions.ChildLoanException;
import exceptions.LentMediaException;
import exceptions.ReservationMediaException;
import exceptions.TooManyLoansException;




import loansManagement.Check;
import loansManagement.Loan;
import mediasManagement.Book;
import mediasManagement.Dvd;
import mediasManagement.Media;
import java.util.List;
import loansManagement.Reservation;

/** 
 * @author virgil
 */
@Entity
@Inheritance (strategy=InheritanceType.JOINED)
@Table(name="subscribers")
@NamedQuery(name="findAllSubscribers", query="select s from Subscriber s")
public class Subscriber {

	/**
	 * @uml.property  name="lastName"
	 */
	private String lastName;

	public Subscriber(String firstName, String lastName, Calendar bornDate, Calendar subscribingDate,String email) {
		this.firstName=firstName;
		this.lastName=lastName;
		this.bornDate=bornDate;
		this.subscribingDate=subscribingDate;
		this.email=email;
		this.entitles= new HashSet<Entitled>();
		this.currentloans= new HashSet<Loan>();
		this.reservations=new HashSet<Reservation>();
	}

	public Subscriber() {
		// TODO Auto-generated constructor stub
	}

	/**
	 * Getter of the property <tt>lastName</tt>
	 * @return  Returns the lastName.
	 * @uml.property  name="lastName"
	 */
	public String getLastName() {
		return lastName;
	}

	/**
	 * Setter of the property <tt>lastName</tt>
	 * @param lastName  The lastName to set.
	 * @uml.property  name="lastName"
	 */
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	/**
	 * @uml.property  name="firstName"
	 */
	private String firstName;

	/**
	 * Getter of the property <tt>firstName</tt>
	 * @return  Returns the firstName.
	 * @uml.property  name="firstName"
	 */
	public String getFirstName() {
		return firstName;
	}

	/**
	 * Setter of the property <tt>firstName</tt>
	 * @param firstName  The firstName to set.
	 * @uml.property  name="firstName"
	 */
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	/**
	 * @uml.property  name="bornDate"
	 */

	private Calendar bornDate;

	/**
	 * Getter of the property <tt>bornDate</tt>
	 * @return  Returns the bornDate.
	 * @uml.property  name="bornDate"
	 */
	public Calendar getBornDate() {
		return bornDate;
	}

	/**
	 * Setter of the property <tt>bornDate</tt>
	 * @param bornDate  The bornDate to set.
	 * @uml.property  name="bornDate"
	 */
	public void setBornDate(Calendar bornDate) {
		this.bornDate = bornDate;
	}

	/**
	 * @uml.property  name="subscribingDate"
	 */
	private Calendar subscribingDate;

	/**
	 * Getter of the property <tt>subscribingDate</tt>
	 * @return  Returns the subscribingDate.
	 * @uml.property  name="subscribingDate"
	 */
	public Calendar getSubscribingDate() {
		return subscribingDate;
	}

	/**
	 * Setter of the property <tt>subscribingDate</tt>
	 * @param subscribingDate  The subscribingDate to set.
	 * @uml.property  name="subscribingDate"
	 */
	public void setSubscribingDate(Calendar subscribingDate) {
		this.subscribingDate = subscribingDate;
	}

	/**
	 * @uml.property  name="numberCreated"
	 */
	private static long numberCreated;
	/**
	 * @uml.property  name="number"
	 */
	
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column(name="SUBSCRIBER_ID",unique = true)
	private long number;

	/**
	 * Getter of the property <tt>number</tt>
	 * @return  Returns the number.
	 * @uml.property  name="number"
	 */
	public long getNumber() {
		return number;
	}

	/**
	 * Setter of the property <tt>number</tt>
	 * @param number  The number to set.
	 * @uml.property  name="number"
	 */
	public void setNumber(long number) {
		this.number = number;
	}


	/** 
	 * @uml.property name="currentloans"
	 * @uml.associationEnd multiplicity="(0 -1)" ordering="true" inverse="lender:loansManagement.Loan"
	 */
	
	@OneToMany(mappedBy = "pk.subscriber", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
	@Cascade({ org.hibernate.annotations.CascadeType.SAVE_UPDATE })
	private Set<Loan> currentloans ;

	/** 
	 * Getter of the property <tt>currentloans</tt>
	 * @return  Returns the currentloans.
	 * @uml.property  name="currentloans"
	 */
	
	public Set<Loan> getCurrentloans() {
		return currentloans;
	}

	
	/**
	 * @uml.property  name="entitles"
	 * @uml.associationEnd  multiplicity="(0 -1)" inverse="mainSubscriber:subscribersManagement.Entitled"
	 * @uml.association  name="subscriberEntitled"
	 */
	@OneToMany(mappedBy = "mainSubscriber")
	private Set<Entitled> entitles;

	/**
	 * Getter of the property <tt>entitles</tt>
	 * @return  Returns the entitles.
	 * @uml.property  name="entitles"
	 */
	public Set<Entitled> getEntitles() {
		return entitles;
	}

	/**
	 * Setter of the property <tt>entitles</tt>
	 * @param entitles  The entitles to set.
	 * @uml.property  name="entitles"
	 */
	public void setEntitles(Set<Entitled> entitles) {
		this.entitles = entitles;
	}
	
	public void addEntitled(Entitled entitled) {
		this.entitles.add(entitled);
	}
	public void removeEntitled(Entitled entitled) {
		this.entitles.remove(entitled);
	}
	
	@Override
	public boolean equals(Object subscriber) {
		if (subscriber == null)
			return false;
		Subscriber s = (Subscriber) subscriber;

		if (s.number == number)
			return true;

		if ((s.firstName == null) || (s.lastName == null)
				|| (s.bornDate == null))
			return false;

		if ((this.firstName == null) || (this.lastName == null)
				|| (this.bornDate == null)||(this.subscribingDate==null))
			return false;

		boolean res = (s.firstName.equals(firstName))
				&& (s.lastName.equals(lastName))
				&& (s.bornDate.equals(bornDate))
				&& (s.subscribingDate.equals(subscribingDate));

		return res;
	}

	/**
	 * @uml.property  name="numberOfDvdLoans"
	 */
	private int numberOfDvdLoans=0;

	/**
	 * Getter of the property <tt>numberOfDvdLoans</tt>
	 * @return  Returns the numberOfDvdLoans.
	 * @uml.property  name="numberOfDvdLoans"
	 */
	public int getNumberOfDvdLoans() {
	
		numberOfDvdLoans = 0;
		for (Loan l : currentloans) {
			if ((!l.isIsReturn()) && l.getExemplary().getMedia() instanceof Dvd) {
				numberOfDvdLoans++;
			}
		}
		return numberOfDvdLoans;
	}

	/**
	 * Setter of the property <tt>numberOfDvdLoans</tt>
	 * @param numberOfDvdLoans  The numberOfDvdLoans to set.
	 * @uml.property  name="numberOfDvdLoans"
	 */
	public void setNumberOfDvdLoans(int numberOfDvdLoans) {
		this.numberOfDvdLoans = numberOfDvdLoans;
	}

	/**
	 * @uml.property  name="numberOfBookLoans"
	 */
	private int numberOfBookLoans ;

	/**
	 * Getter of the property <tt>numberOfBookLoans</tt>
	 * @return  Returns the numberOfBookLoans.
	 * @uml.property  name="numberOfBookLoans"
	 */
	public int getNumberOfBookLoans() {
		numberOfBookLoans = 0;
		for (Loan l : currentloans) {
			if ((!l.isIsReturn()) && l.getExemplary().getMedia() instanceof Book) {
				numberOfBookLoans++;
			}
		}
		return numberOfBookLoans;
	}

	/**
	 * Setter of the property <tt>numberOfBookLoans</tt>
	 * @param numberOfBookLoans  The numberOfBookLoans to set.
	 * @uml.property  name="numberOfBookLoans"
	 */
	public void setNumberOfBookLoans(int numberOfBookLoans) {
		this.numberOfBookLoans = numberOfBookLoans;
	}

	public void lend(Loan loan) throws BadParametersException, LentMediaException, ChildLoanException {
		// TODO Auto-generated method stub
		if (loan == null)
			throw new BadParametersException();
         
		if (!(Check.canLend(this, loan.getMedia())))
			throw new LentMediaException("Impossible d'emprunter ce media");
		
		this.currentloans.add(loan);
		
         if (loan.getMedia() instanceof Book){
        	 numberOfBookLoans++;
         }
         if (loan.getMedia() instanceof Dvd){
        	 numberOfDvdLoans++;
         }
         Session session= HibernateUtil.openSession();
         session.merge(this);
	}

	public String toString() {
		return firstName +" "+ lastName;
		
	}

	/**
	 * @uml.property  name="reservations"
	 * @uml.associationEnd  multiplicity="(0 -1)" inverse="subscriber:loansManagement.Reservation"
	 */
	@OneToMany(mappedBy = "pk.subscriber", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
	@Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE ,
	org.hibernate.annotations.CascadeType.DELETE_ORPHAN})
	
	private Set<Reservation> reservations;

	/**
	 * Getter of the property <tt>reservations</tt>
	 * @return  Returns the reservations.
	 * @uml.property  name="reservations"
	 */
	public Set<Reservation> getReservations() {
		return reservations;
	}

	/**
	 * Setter of the property <tt>reservations</tt>
	 * @param reservations  The reservations to set.
	 * @uml.property  name="reservations"
	 */
	public void setReservations(Set<Reservation> reservations) {
		this.reservations = reservations;
	}

	public void reserve(Reservation reservation) throws BadParametersException, ReservationMediaException, ChildLoanException {
		// TODO Auto-generated method stub
		if(reservation==null)
			throw new BadParametersException();
		
		if(!Check.canReserve(this,reservation.getMedia()))
			throw new ReservationMediaException();
		
		this.reservations.add(reservation);
		Session session= HibernateUtil.openSession();
        session.merge(this);
	}

	/** 
	 * Setter of the property <tt>currentloans</tt>
	 * @param currentloans  The currentloans to set.
	 * @uml.property  name="currentloans"
	 */
	public void setCurrentloans(Set<Loan> currentloans) {
		this.currentloans = currentloans;
	}

	/**
	 * @uml.property  name="email"
	 */
	private String email;

	/**
	 * Getter of the property <tt>email</tt>
	 * @return  Returns the email.
	 * @uml.property  name="email"
	 */
	public String getEmail() {
		return email;
	}

	/**
	 * Setter of the property <tt>email</tt>
	 * @param email  The email to set.
	 * @uml.property  name="email"
	 */
	public void setEmail(String email) {
		this.email = email;
	}	

		
}
